using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using LDNS.Models;
using NLog;

namespace LDNS.Utilities
{
    /// <summary>
    /// DNS缓存管理器（单例模式）
    /// 负责管理DNS查询缓存的所有操作
    /// 作者：huobing
    /// </summary>
    public class DnsCacheManager
    {
        #region 单例实现

        /// <summary>
        /// 单例实例
        /// </summary>
        private static DnsCacheManager? _instance;
        
        /// <summary>
        /// 锁对象，用于线程安全的单例初始化
        /// </summary>
        private static readonly object _lock = new();
        
        /// <summary>
        /// 获取DnsCacheManager的单例实例
        /// </summary>
        public static DnsCacheManager Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_lock)
                    {
                        if (_instance == null)
                        {
                            _instance = new DnsCacheManager();
                        }
                    }
                }
                return _instance;
            }
        }

        #endregion

        #region 字段和属性

        private readonly ConcurrentDictionary<string, DnsCacheItem> _dnsCache;
        private readonly DnsCacheStatistics _cacheStatistics;
        private Timer? _cacheCleanupTimer;
        private readonly Logger _logger = LogManager.GetCurrentClassLogger();
        private readonly object _cacheLock = new();

        /// <summary>
        /// 获取DNS缓存记录数量
        /// </summary>
        public int CacheCount => _dnsCache.Count;

        /// <summary>
        /// 获取DNS缓存统计信息
        /// </summary>
        public DnsCacheStatistics CacheStatistics
        {
            get
            {
                // 更新所有缓存统计数据
                _cacheStatistics.TotalItems = _dnsCache.Count;
                _cacheStatistics.TotalQueries = _cacheStatistics.HitCount + _cacheStatistics.MissCount;
                
                // 计算有效和过期缓存项数量
                var now = DateTime.Now;
                _cacheStatistics.ValidItems = _dnsCache.Count(kv => kv.Value.ExpiresAt > now);
                _cacheStatistics.ExpiredItems = _cacheStatistics.TotalItems - _cacheStatistics.ValidItems;
                
                _cacheStatistics.LastCleanup = DateTime.Now;
                return _cacheStatistics;
            }
        }

        /// <summary>
        /// 获取DNS缓存记录
        /// </summary>
        public Dictionary<string, DnsCacheItem> DnsCache
        {
            get
            {
                CleanExpiredCache();
                return new(_dnsCache);
            }
        }

        #endregion

        #region 构造函数

        /// <summary>
        /// 私有构造函数，防止外部实例化
        /// </summary>
        private DnsCacheManager()
        {
            _dnsCache = new ConcurrentDictionary<string, DnsCacheItem>();
            _cacheStatistics = new DnsCacheStatistics();
        }

        #endregion

        #region 缓存操作方法

        /// <summary>
        /// 清空DNS缓存
        /// </summary>
        public void ClearCache()
        {
            lock (_cacheLock)
            {
                _dnsCache.Clear();
                _cacheStatistics.TotalSize = 0; // 重置缓存大小
                _logger.Info("DNS缓存已清空");
            }
        }

        /// <summary>
        /// 添加DNS缓存项
        /// </summary>
        /// <param name="domain">域名</param>
        /// <param name="cacheItem">DNS缓存项</param>
        /// <returns>是否添加成功</returns>
        public bool AddDnsCacheItem(string domain, DnsCacheItem cacheItem)
        {
            if (string.IsNullOrWhiteSpace(domain) || cacheItem == null)
            {
                return false;
            }

            try
            {
                string key = domain.ToLower();
                
                // 如果已存在相同的缓存项，先减去旧项的大小
                if (_dnsCache.ContainsKey(key))
                {
                    _cacheStatistics.TotalSize -= CalculateCacheItemSize(key, _dnsCache[key]);
                }
                
                // 添加新缓存项并增加大小
                _dnsCache[key] = cacheItem;
                _cacheStatistics.TotalSize += CalculateCacheItemSize(key, cacheItem);

                _logger.Info($"添加DNS缓存项: {domain} ({cacheItem.RecordType}) -> {cacheItem.IP}, 来源: {cacheItem.Source}");
                return true;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, $"添加DNS缓存项失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 移除DNS缓存项
        /// </summary>
        /// <param name="domain">域名</param>
        /// <returns>是否移除成功</returns>
        public bool RemoveDnsCacheItem(string domain)
        {
            if (string.IsNullOrWhiteSpace(domain))
            {
                return false;
            }

            try
            {
                string key = domain.ToLower();
                bool removed = _dnsCache.TryRemove(key, out var removedItem);

                if (removed && removedItem != null)
                {
                    // 减去被移除项的大小
                    _cacheStatistics.TotalSize -= CalculateCacheItemSize(key, removedItem);
                    _logger.Info($"移除DNS缓存项: {domain}");
                }

                return removed;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, $"移除DNS缓存项失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 获取DNS缓存项
        /// </summary>
        /// <param name="domain">域名</param>
        /// <returns>DNS缓存项，如果不存在或已过期则返回null</returns>
        public DnsCacheItem? GetDnsCacheItem(string domain)
        {
            if (string.IsNullOrWhiteSpace(domain))
            {
                _cacheStatistics.MissCount++;
                return null;
            }

            try
            {
                string key = domain.ToLower();
                if (_dnsCache.TryGetValue(key, out var cacheItem))
                {
                    // 检查缓存是否过期
                    if (cacheItem.ExpiresAt > DateTime.Now)
                    {
                        _cacheStatistics.HitCount++;
                        return cacheItem;
                    }
                    else
                    {
                        // 移除过期缓存并减去大小
                        if (_dnsCache.TryRemove(key, out var removedItem))
                        {
                            _cacheStatistics.TotalSize -= CalculateCacheItemSize(key, removedItem);
                            _logger.Info($"自动移除过期缓存项: {domain}");
                        }
                    }
                }

                _cacheStatistics.MissCount++;
                return null;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, $"获取DNS缓存项失败: {ex.Message}");
                _cacheStatistics.MissCount++;
                return null;
            }
        }

        /// <summary>
        /// 清理过期缓存
        /// </summary>
        /// <returns>清理的缓存项数量</returns>
        public int CleanExpiredCache()
        {
            int removedCount = 0;
            var now = DateTime.Now;

            try
            {
                var expiredKeys = _dnsCache.Where(kv => kv.Value.ExpiresAt < now).Select(kv => kv.Key).ToList();

                foreach (var key in expiredKeys)
                {
                    if (_dnsCache.TryRemove(key, out var removedItem))
                    {
                        // 减去过期项的大小
                        _cacheStatistics.TotalSize -= CalculateCacheItemSize(key, removedItem);
                        removedCount++;
                    }
                }

                if (removedCount > 0)
                {
                    _logger.Info($"清理过期缓存: 移除了 {removedCount} 个过期缓存项");
                }

                return removedCount;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, $"清理过期缓存失败: {ex.Message}");
                return 0;
            }
        }

        /// <summary>
        /// 获取缓存命中率
        /// </summary>
        /// <returns>命中率百分比</returns>
        public double GetCacheHitRate()
        {
            if (_cacheStatistics.HitCount + _cacheStatistics.MissCount == 0)
            {
                return 0.0;
            }

            return (_cacheStatistics.HitCount / (double)(_cacheStatistics.HitCount + _cacheStatistics.MissCount)) * 100.0;
        }

        /// <summary>
        /// 计算单个缓存项的大小（字节）
        /// </summary>
        /// <param name="key">缓存键</param>
        /// <param name="item">缓存项</param>
        /// <returns>缓存项大小（字节）</returns>
        private long CalculateCacheItemSize(string key, DnsCacheItem item)
        {
            // 简化计算：估算字符串和对象的大小
            // 实际项目中可以使用更精确的内存计算方法
            long size = 0;
            
            // 计算键的大小（假设每个字符2字节，UTF-16）
            size += key.Length * 2;
            
            // 计算各项内容的大小
            if (!string.IsNullOrEmpty(item.IP)) size += item.IP.Length * 2;
            if (!string.IsNullOrEmpty(item.RecordType)) size += item.RecordType.Length * 2;
            if (!string.IsNullOrEmpty(item.Source)) size += item.Source.Length * 2;
            if (!string.IsNullOrEmpty(item.UpstreamDns)) size += item.UpstreamDns.Length * 2;
            
            // 对象本身的固定开销（粗略估计）
            size += 100; // 包括时间戳、TTL等字段的大小
            
            return size;
        }

        /// <summary>
        /// 根据上游DNS服务器IP清除缓存（清除由这些上游DNS产生的域名缓存）
        /// </summary>
        /// <param name="dnsServers">上游DNS服务器IP列表</param>
        public void ClearCacheByUpstreamDnsServers(IEnumerable<string>? dnsServers)
        {
            if (dnsServers == null || !dnsServers.Any())
            {
                return;
            }

            try
            {
                var dnsServerSet = new HashSet<string>(dnsServers, StringComparer.OrdinalIgnoreCase);
                int removedCount = 0;

                // 查找并移除由指定上游DNS服务器产生的缓存项
                var keysToRemove = _dnsCache.Where(kv => kv.Value.Source == "upstream" && 
                                                         dnsServerSet.Contains(kv.Value.UpstreamDns ?? string.Empty))
                                          .Select(kv => kv.Key)
                                          .ToList();

                foreach (var key in keysToRemove)
                {
                    if (_dnsCache.TryRemove(key, out _))
                    {
                        removedCount++;
                    }
                }

                if (removedCount > 0)
                {
                    _logger.Info($"根据上游DNS清除缓存: 移除了 {removedCount} 个缓存项");
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex, $"根据上游DNS清除缓存失败: {ex.Message}");
            }
        }

        #endregion

        #region 缓存定时器管理

        /// <summary>
        /// 启动缓存清理定时器
        /// </summary>
        /// <param name="intervalMinutes">清理间隔（分钟）</param>
        public void StartCacheCleanupTimer(int intervalMinutes = 5)
        {
            try
            {
                // 停止现有定时器（如果有）
                StopCacheCleanupTimer();

                // 创建新的定时器
                _cacheCleanupTimer = new System.Threading.Timer(
                    _ => CleanExpiredCache(),
                    null,
                    TimeSpan.FromMinutes(intervalMinutes),
                    TimeSpan.FromMinutes(intervalMinutes));

                _logger.Info($"自动缓存清理定时器已启动（每{intervalMinutes}分钟清理一次过期缓存）");
            }
            catch (Exception ex)
            {
                _logger.Error(ex, $"启动缓存清理定时器失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 停止缓存清理定时器
        /// </summary>
        public void StopCacheCleanupTimer()
        {
            if (_cacheCleanupTimer != null)
            {
                try
                {
                    _cacheCleanupTimer.Dispose();
                    _cacheCleanupTimer = null;
                    _logger.Info("缓存清理定时器已停止");
                }
                catch (Exception ex)
                {
                    _logger.Error(ex, $"停止缓存清理定时器失败: {ex.Message}");
                }
            }
        }

        #endregion

        #region 缓存定时器管理

        /// <summary>
        /// 获取域名的所有缓存IP地址
        /// </summary>
        /// <param name="domain">域名</param>
        /// <returns>IP地址列表，如果没有缓存则返回空列表</returns>
        public List<string> GetAllCachedIpsForDomain(string domain)
        {
            if (string.IsNullOrWhiteSpace(domain))
            {
                return new List<string>();
            }

            try
            {
                // 获取缓存项
                var cacheItem = GetDnsCacheItem(domain);
                
                // 如果缓存项存在且未过期，返回其IP地址列表
                if (cacheItem != null && cacheItem.IPs != null)
                {
                    return cacheItem.IPs.Distinct().ToList();
                }
                
                return new List<string>();
            }
            catch (Exception ex)
            {
                _logger.Error(ex, $"获取域名缓存IP失败: {ex.Message}");
                return new List<string>();
            }
        }

        #endregion
    }
}